home *** CD-ROM | disk | FTP | other *** search
/ Aminet 6 / Aminet 6 - June 1995.iso / Aminet / misc / sci / RARS_Amiga_2.lha / RARS / carz.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1995-03-31  |  41.3 KB  |  1,085 lines

  1. // CARZ.CPP - simulated robot car racing - begin development Dec. '94
  2. // by Mitchell E. Timin, State College, PA
  3. // see CAR.H & TRACK.H for class and structure declarations
  4. // This version is for Borland C++, version 3.1, and is for DOS
  5. // This is part of version 0.39 of RARS (Robot Auto Racing Simulation)
  6. // ver. 0.1 release January 12, 1995
  7. // ver. 0.2 1/23/95
  8. // ver. 0.3 2/7/95
  9. // ver. 0.39 3/6/95
  10.  
  11. #include <math.h>
  12. #include <ctype.h>
  13. #include <stdlib.h>
  14. #include <string.h>
  15. #include <iostream.h>
  16. #include "car.h"
  17. #include "track.h"
  18. #include "os.h"
  19.  
  20. static const double PM = 1e5;    // Power, Maximum, 100,000 ft. lb. per sec
  21. static const double g = 32.2;    // acceleration due to gravity, ft./sec^2
  22. static const double DRAG_CON = .0065;  // air drag, lb. per (ft/sec)^2
  23. static const double M = 80;  // mass, slugs   (2600 lb.)
  24. static const double STARTING_SPEED = 40.0;  // cars start at this speed, ft/sec
  25. static const double REVERSE_GEAR_LIMIT = 20; // ft/sec max to allow reverse
  26.  
  27. // global variables:
  28. // These are set by the command line arguments: (see get_args())
  29. int lap_count;       // length of the race in laps
  30. int car_count;       // how many cars in the race
  31. int race_count;      // how many races?
  32. int real_speed;      // if non-zero, PC clock will control speed of race
  33. int no_display;      // if non-zero, race will be invisible
  34. int keep_order;      // if this is 0, then starting order will be re-arranged
  35.  
  36. char *glob_name;     // will hold name of each robot driver (one at a time)
  37. double length;       // total lenth of track (average of inner and outer rails)
  38. int done_count;  // incremented by each car that finishes the race
  39. int out_count;   // incremented by each car that crashes
  40. double time_count;  // elapsed time, seconds
  41.  
  42. // These are the control or "driver" programs which compete in the race:
  43. con_vec cntrl0(situation);
  44. con_vec cntrl1(situation);  // See drivers[], below.
  45. con_vec cntrl2(situation);
  46. con_vec cntrl3(situation);
  47. con_vec cntrl4(situation);
  48. con_vec cntrl5(situation);
  49. con_vec cntrlb0(situation);
  50. con_vec cntrlb(situation);
  51. con_vec cntrlR(situation);
  52. con_vec bill(situation);
  53. con_vec dynamic(situation);
  54. con_vec arelys(situation);
  55. con_vec Ramdu2(situation);
  56.  
  57. Car* pcar[MAXCARS];                // array of pointers to the various cars
  58. char* nam_ptr[MAXCARS];            // array of pointers to their name strings
  59. char* namptr[MAXCARS];             // re-arrangement of nam_ptr[]
  60. int* order;                        // will point to array of positions
  61. int* new_data;                     // points to array of new data indicators
  62. int lap[MAXCARS];                  // set, then cleared as each lap completes
  63. colors car_colors[MAXCARS];  // There should be MAXCARS of these color pairs:
  64.  
  65. // This is the permanent array of available drivers.
  66. // Their order here determines their car colors.
  67. con_vec(*drovers[])(situation) = {   // pointers to their control programs
  68.    cntrlR, Ramdu2, cntrl1, cntrl2, cntrlb0, arelys,
  69.    cntrl3, cntrlb, cntrl0, cntrl4, cntrl5,  bill,
  70.    dynamic, dynamic  };
  71.  
  72. // This is the array used during the race.  (It will be re-arranged.)
  73. // The order here determines their starting positions.
  74. con_vec(*drivers[])(situation) = {
  75.    cntrlR, Ramdu2, cntrl1, cntrl2, cntrlb0, arelys,
  76.    cntrl3, cntrlb, cntrl0, cntrl4, cntrl5,  bill,
  77.    dynamic, dynamic  };
  78.  
  79. void report_overall(int, int, char**);       // in REPORT.CPP
  80. void report_results(int, int*, char**, Car**);      // in REPORT.CPP
  81. void RAM_report(void);                       // in REPORT.CPP
  82.  
  83. // these things are defined in DRAW.CPP:
  84. extern int xcon(double x);        // convert x coordinate to pixels
  85. extern int ycon(double y);        // convert y coordinate to pixels
  86. extern int round(double given);   // convert double to int by rounding
  87. extern void draw_arc(double,double,double,double,double);
  88. extern void make_dec_string(char* out, double input);
  89. extern void get_avg_spd(int i, char* out);
  90. extern void get_max_spd(int i, char* out);
  91. extern double drawpath(double, double, double, segment*);
  92. extern int drawcar(double, double, double, int, int);
  93. extern void lapper(int which, int lap);  // shows lap count on scoreboard,
  94. extern void resume_normal_display(void);
  95. extern void leaders(int);
  96. extern void scoreboard(void);
  97. extern void graph_setup(void);
  98. extern void refresh_finish_line(void);
  99.  
  100. // call repeatedly to change direction by 180 degrees:
  101. inline void reverse(double v, double* alpha_ptr, double* vc_ptr)
  102. {
  103.    if(v > 10.0)         // This is algorithm to reverse velocity vector:
  104.        *vc_ptr = 0.0;      // if not going very slow, brake hard
  105.     else
  106.        *vc_ptr = -15.0;    // when going slow enough, put 'er in reverse!
  107.     *alpha_ptr = 0.0;      // don't turn.
  108. }
  109.  
  110. /* This routine analyses the parameters to determine if the car is in an
  111.    abnormal situation.  If so, it returns non-zero & sets the result
  112.    vector so as to free the car.  If all is normal it returns zero.
  113.    This is a service for the robot drivers; it is only called by them. */
  114. int stuck(int backward, double v, double vn, double to_lft,
  115.                              double to_rgt, double* alpha_ptr, double* vc_ptr)
  116. {
  117.    if(to_lft < 0.0)        // If over the left wall, 
  118.       if(vn > .5 * v)  {  // test for more than 30 degrees off course
  119.          reverse(v, alpha_ptr, vc_ptr);
  120.          return 1;
  121.       }
  122.       else if(vn > -.5 * v && backward) {  // or going well backward
  123.          reverse(v, alpha_ptr, vc_ptr);
  124.          return 1;
  125.       }
  126.       else if(vn < -.5 * v) {    // heading away from wall,
  127.          *alpha_ptr = .03;       // turn to left
  128.          *vc_ptr = (.66667 * v + 10.0);  // accelerate toward 30 fps
  129.          return 1;
  130.       }
  131.       else {
  132.          *alpha_ptr = -.03;       // turn to right
  133.          *vc_ptr = (.66667 * v + 10.0);  // accelerate toward 30 fps
  134.          return 1;
  135.       }
  136.    else if(to_rgt < 0.0)  // if over the right wall:
  137.       if(vn < -.5 * v)  {  // test for more than 30 degrees off course
  138.          reverse(v, alpha_ptr, vc_ptr);
  139.          return 1;
  140.       }
  141.       else if(vn < .5 * v && backward) {  // or going well backward
  142.          reverse(v, alpha_ptr, vc_ptr);
  143.          return 1;
  144.       }
  145.       else if(vn > .5 * v) {    // heading away from wall,
  146.          *alpha_ptr = -.03;       // turn to right
  147.          *vc_ptr = .66667 * v + 10.0;  // accelerate toward 30 fps
  148.          return 1;
  149.       }
  150.       else {
  151.          *alpha_ptr = .03;       // turn to left
  152.          *vc_ptr = .66667 * v + 10.0;  // accelerate toward 30 fps
  153.          return 1;
  154.       }
  155.    else if(backward)
  156.      if(vn > .866 * v)  { // you are going more-or-less sideways left
  157.         *alpha_ptr = -.03;
  158.         *vc_ptr = .66667 * v + 10;
  159.          return 1;
  160.      }
  161.      else if(vn < -.866 * v) {  // you are going more-or-less sideways rt.
  162.         *alpha_ptr = .03;
  163.         *vc_ptr = .66667 * v + 10;
  164.          return 1;
  165.      }
  166.      else {
  167.       reverse(v, alpha_ptr, vc_ptr);
  168.       return 1;
  169.      }
  170.    else if(v < 15) {  // nothing wrong except you are going very slow:
  171.       if(to_rgt > to_lft)     // you are on left side of track
  172.          if(vn < -.7 * v)     // and you are not heading very much to right
  173.             *alpha_ptr = -.03;
  174.          else
  175.             *alpha_ptr = .03;
  176.       else                   // you are on the right side,
  177.          if(vn > .7 * v)     // and you are not heading very much to left
  178.             *alpha_ptr = .03;
  179.          else
  180.             *alpha_ptr = -.03;
  181.      *vc_ptr = .66667 * v + 10;    // acellerate moderately
  182.       return 1;
  183.    }
  184.    return 0;   // We get here only if all is normal.
  185. }
  186.  
  187. // a sorting routine that's fast when things are already in order:
  188. // This routine updates the order[] array which has the position of each
  189. // car.  order[0] is the ID of the leader, order[1] is in second place, etc.
  190. // Also, the new_data[] array is set to show which positions have changed.
  191. void sortem(int num_disp)
  192. {
  193.    int i, temp;
  194.    int old_order[MAXCARS];
  195.  
  196.    for(i=0; i<num_disp; i++)     // copy first part of order[] array
  197.       old_order[i] = order[i];
  198.  
  199.    for(i=0; i<car_count-1; i++) // the while loop below does not usually repeat:
  200.       while(farther(pcar[order[i]], pcar[order[i+1]]))  {
  201.          temp = order[i];       // When a car passes another, we swap positions
  202.          order[i] = order[i+1];
  203.          order[i+1] = temp;
  204.          if(i > 0)              // now we have to see if it passed another car
  205.             --i;
  206.       }
  207.  
  208.    for(i=0; i<num_disp; i++)     // see what has changed and flag it:
  209.       new_data[i] = (old_order[i] != order[i]);
  210. }
  211.  
  212. inline double vec_mag(double x, double y)  // sqrt of sum of squares
  213. {                                          // (used for vector magnitude)
  214.    return sqrt(x * x + y * y);
  215. }
  216.  
  217. // Comparison routine for determining who's ahead: (i.e., sorting)
  218. // Returns 0 iff car0 has gone farther than car1, 1 otherwise.
  219. int farther(Car* car0, Car* car1)
  220. {
  221.    int blok0, blok1;  /* A block is the same as a track segment except for
  222.                          segment 0.  For segment 0, the block is 0 when the
  223.                          car is past the finish line.  When the car is heading
  224.                          toward the finish line the block is NSEG.  */
  225.    blok0 = car0->seg_id;
  226.    if(!blok0)
  227.       if(car0->to_end > from_start_to_seg1)
  228.          blok0 = NSEG;
  229.    blok1 = car1->seg_id;
  230.    if(!blok1)
  231.       if(car1->to_end > from_start_to_seg1)
  232.          blok1 = NSEG;
  233.    if(car0->laps > car1->laps)
  234.       return 0;
  235.    else if(car0->laps < car1->laps)
  236.       return 1;                              // else laps are equal
  237.    else if(blok0 > blok1)             
  238.       return 0;
  239.    else if(blok1 > blok0)
  240.       return 1;                            // else they are in the same blok:
  241.    else if(car0->to_end <= car1->to_end)
  242.       return 0;
  243.    else return 1;        // returns 1 in case of total equality
  244. }
  245.  
  246. inline int incseg(int seg)       // returns the next segment of the track
  247. {                                // (i.e. 0 yields 1, 1 yields 2, but
  248.    if(++seg == NSEG)             // NSEG-1 yields 0)
  249.       seg = 0;
  250.    return seg;
  251. }
  252.  
  253. // This is the model of the track friction force on the tire.  This force
  254. // provides propulsion, cornering, and braking.  Force is assumed to depend
  255. // only on the slip velocity, rising very rapidly with small slip velocity,
  256. // and then asymtotically approaching an upper limit.  (This is similar to
  257. // tires on unpaved surfaces.)  ALSO: There is randomness in the force at
  258. // small slip velocities!
  259. const double MYU_MAX = 1.0;  // maximum coeficient of friction, tire vs. track:
  260. inline double rfriction(double slip)     // returns the coef. of friction,
  261. {                                       // given the slip speed, ft. per sec.
  262.    double slipping;     // slip speed at which half maximum force is reached
  263.  
  264.    slipping = 1.5 + (double)rand()/RAND_MAX;    // range is 1.5 to 2.5 fps
  265.    return (MYU_MAX * slip)/(slipping + slip);
  266. }
  267.  
  268. // This is a version of the above without any randomness.  They produce the
  269. // same result when the random variable in rfriction() has its mean value.
  270. const double SLIPPING = 2.0;
  271. inline double friction(double slip)     // returns the coef. of friction,
  272. {                                       // given the slip speed, ft. per sec.
  273.    return (MYU_MAX * slip)/(SLIPPING + slip);
  274. }
  275.  
  276. // Member Functions of the Car Class:
  277.  
  278. // returns laps and passes seg_id and distance to next segment via pointers:
  279. inline int Car::where_is_it(int* seg_id_addr, double* to_end_addr)
  280. {
  281.    *seg_id_addr = seg_id;
  282.    *to_end_addr = to_end;
  283.    return laps;
  284. }
  285.  
  286. // limits the rate of change and maximum value of the angle of attack:
  287. double alpha_limit(double was,       // This is what alpha was
  288.                    double request)   // The robot wants this alpha
  289. {
  290.    const double MAX_RATE = 4.6;  // maximum radians per second possible
  291.    const double MAX_ALPHA= 1.0;   // maximum radians possible
  292.  
  293.    double alpha;                 // the result to return
  294.  
  295.    if(request - was > MAX_RATE * delta_time)   // want more positive alpha
  296.       alpha = was + MAX_RATE * delta_time;
  297.    else if(was - request > MAX_RATE * delta_time)  // want more negative alpha
  298.       alpha = was - MAX_RATE * delta_time;
  299.    else
  300.       alpha = request;
  301.  
  302.    if(alpha > MAX_ALPHA)
  303.       alpha = MAX_ALPHA;
  304.    else if(alpha < -MAX_ALPHA)
  305.       alpha = -MAX_ALPHA;
  306.  
  307.    return alpha;
  308. }
  309.  
  310. // the purpose of control() is to call the car's driver function, and
  311. // then to return the steering and throttle settings produced by
  312. // that routine.
  313. void Car::control(situation s)  // calls the control function via the pointer
  314. {                               // that was installed in the car object by
  315.    con_vec output;              // by the constructor.
  316.    con_vec (*func_ptr)(situation);
  317.  
  318.    if(out)
  319.       output.alpha = output.vc = 0.0;  // no action if out of race
  320.    else {
  321.       s.data_ptr = data_ptr;
  322.       s.starting = starting;  // startup signal for the robot
  323.       starting = 0;
  324.       func_ptr = cntrl;
  325.       output = (*func_ptr)(s);   // call the robot driver to set alpha and vc
  326.    }
  327.    alpha = output.alpha;   vc = output.vc;  // set private vars in car object
  328. }
  329.  
  330. double power_excess(double vc, double sine, double cosine, double v)
  331. {
  332.    double Ln, Lt;      // normal and tangential components of slip vector
  333.    double l;           // magnitude of slip vector, ft. per sec.
  334.    double F;           // force on car from track, lb.
  335.    double Fn, Ft;      // tangential and normal (to car's path) force components
  336.  
  337.    Ln = -vc * sine;   Lt = v - vc * cosine; // vector sum to compute slip vector
  338.    l = vec_mag(Lt, Ln);                     // compute slip speed
  339.    F = M * g * friction(l);                 // compute friction force from track
  340.    if(l < .0001)              // to prevent possible division by zero
  341.       Fn = Ft = 0.0;
  342.    else  {
  343.       Fn = -F * Ln/l;      // compute components of force vector
  344.       Ft = -F * Lt/l;
  345.    }
  346.    // compute power delivered:
  347.    return (vc < 0.0 ? -vc : vc) * (Ft * cosine + Fn * sine) - .9975*PM;
  348. }
  349.  
  350. inline double abs(double x)
  351. {
  352.    if(x < 0.0)
  353.       return -x;
  354.    else
  355.       return x;
  356. }
  357.  
  358. inline double SIGN(double a, double b)
  359. {
  360.    return b >= 0.0 ? abs(a) : -abs(a);
  361. }
  362.  
  363. // This routine was adapted from the book "Numerical Recipes in C" by
  364. // Press, et. al.  It searches for a value of vc which causes the
  365. // power to be very close to the maximum available.
  366. // (This is Brent's method of root finding.)  x1 and x2 are values of
  367. // vc which bracket the root.  b represents the variable vc.
  368. double zbrent(double sine, double cosine, double v, double x1, double x2, double tol)
  369. {
  370.    const int ITMAX = 20;
  371.    const double EPS = 1.0e-8;
  372.     int iter;
  373.     double a=x1, b=x2, c=x2, d,e,min1,min2;
  374.     double fa=power_excess(a, sine, cosine, v);
  375.     double fb=power_excess(b, sine, cosine, v);
  376.    double fc,p,q,r,s,tol1,xm;
  377.    double Ln, Lt;      // normal and tangential components of slip vector
  378.    double l;           // magnitude of slip vector, ft. per sec.
  379.    double F;           // force on car from track, lb.
  380.    double Fn, Ft;      // tangential and normal (to car's path) force components
  381.  
  382.     if ((fa > 0.0 && fb > 0.0) || (fa < 0.0 && fb < 0.0))
  383.         return b;              // This should never happen.
  384.     fc=fb;
  385.     for (iter=1;iter<=ITMAX;iter++) {
  386.         if ((fb > 0.0 && fc > 0.0) || (fb < 0.0 && fc < 0.0)) {
  387.             c=a;
  388.             fc=fa;
  389.             e=d=b-a;
  390.         }
  391.         if (abs(fc) < abs(fb)) {
  392.             a=b;
  393.             b=c;
  394.             c=a;
  395.             fa=fb;
  396.             fb=fc;
  397.             fc=fa;
  398.         }
  399.         tol1=2.0*EPS*abs(b)+0.5*tol;
  400.         xm=0.5*(c-b);
  401.         if (abs(xm) <= tol1 || fb == 0.0)
  402.          return b;
  403.         if (abs(e) >= tol1 && abs(fa) > abs(fb)) {
  404.             s=fb/fa;
  405.             if (a == c) {
  406.                 p=2.0*xm*s;
  407.                 q=1.0-s;
  408.             } else {
  409.                 q=fa/fc;
  410.                 r=fb/fc;
  411.                 p=s*(2.0*xm*q*(q-r)-(b-a)*(r-1.0));
  412.                 q=(q-1.0)*(r-1.0)*(s-1.0);
  413.             }
  414.             if (p > 0.0) q = -q;
  415.             p=abs(p);
  416.             min1=3.0*xm*q-abs(tol1*q);
  417.             min2=abs(e*q);
  418.             if (2.0*p < (min1 < min2 ? min1 : min2)) {
  419.                 e=d;
  420.                 d=p/q;
  421.             } else {
  422.                 d=xm;
  423.                 e=d;
  424.             }
  425.         } else {
  426.             d=xm;
  427.             e=d;
  428.         }
  429.         a=b;
  430.         fa=fb;
  431.         if (abs(d) > tol1)
  432.             b += d;
  433.         else
  434.             b += SIGN(tol1,xm);
  435.       Ln = -b * sine;   Lt = v - b * cosine; // vector sum to compute slip vector
  436.       l = vec_mag(Lt, Ln);                     // compute slip speed
  437.       F = M * g * friction(l);                 // compute friction force from track
  438.       if(l < .0001)              // to prevent possible division by zero
  439.          Fn = Ft = 0.0;
  440.       else  {
  441.          Fn = -F * Ln/l;      // compute components of force vector
  442.          Ft = -F * Lt/l;
  443.       }
  444.       // compute power delivered:
  445.       fb = (b < 0.0 ? -b : b) * (Ft * cosine + Fn * sine) - .9975*PM;
  446.     }
  447.     return b;
  448. }
  449.  
  450. // Simulates the physics, moves the car by changing the state variables.
  451. // The angle "alpha" is the angle between the car's orientation angle and
  452. // its velocity vector.  (like angle of attack of an aircraft)
  453. // Cornering force depends on alpha.  (It is also thought of as a slip angle.)
  454. // "Slip" refers to wheel vs. track motion.
  455. // "vc" is the speed of the bottom of the wheel relative to the car.
  456. // This model is like a four-wheel drive car, since the forces are not
  457. // computed separately for front and rear wheels.
  458. void Car::move_car()
  459. {
  460.    double D;        // force on car from air, lb.
  461.    double Fn, Ft;      // normal & tangential components of track force vector
  462.    double P;           // power delivered to track, ft. lb. per sec.
  463.    double v;           // car's speed
  464.    double Ln, Lt;      // normal and tangential components of slip vector
  465.    double l;           // magnitude of slip vector, ft. per sec.
  466.    double F;           // force on car from track, lb.
  467.    double x_a, y_a;    // accelleration components in x & y directions
  468.    double sine, cosine, separation, dx, dy;
  469.    double pvec_x, pvec_y;   // pointing vector of car (velocity vec + alpha)
  470.    int i, other_seg;
  471.    double dot, mag_prod, temp;
  472.    double rel_xdot, rel_ydot;   // velocity relative to other car being hit
  473.  
  474.    if(out)            // This gets set if the car is stuck and off the track
  475.       return;
  476.    v = vec_mag(xdot, ydot);                     // the car's speed, feet/sec
  477.    if(v > speed_max)                            // keep track of max speed
  478.         speed_max = v;
  479.    // limit how fast alpha can change, and its maximum value
  480.    alpha = alpha_limit(prev_alpha, alpha);
  481.    prev_alpha = alpha;
  482.    sine = sin(alpha);   cosine = cos(alpha);    // alpha is angle of attack
  483.    // don't allow reverse gear
  484.    if(vc < 0.0)    // This would be a reverse gear request
  485.       if(v > REVERSE_GEAR_LIMIT)   // if going too fast
  486.          vc = 0.0;                      // make it maximum braking
  487.  
  488.    dead_ahead = 0;   // will be set if there is a car more-or-less dead ahead
  489.    for(i=0; i<car_count; i++) {     // check for cars nearby or bumping
  490.       if(i == which) continue;         // ignore oneself
  491.       other_seg = pcar[i]->seg_id;     // only consider present and next segment
  492.       if(!(seg_id == other_seg || incseg(seg_id) == other_seg))
  493.           continue;
  494.       dx = pcar[i]->x - x;      // components of vector to other car
  495.       dy = pcar[i]->y - y;
  496.       separation = vec_mag(dx, dy);      // distance to the other car
  497.       if(separation > 2.5 * CARLEN)      // ignore cars farther away than this
  498.          continue;
  499.       // Is there a car dead ahead?  "dead ahead" means within 2.5 car lengths
  500.       // AND within a few degrees of pointing vector  (arcos(.94) == 20 deg.)
  501.       // First compute pointing vector by rotating velocity vector by alpha:
  502.       pvec_x = xdot * cosine - ydot * sine;  // components of pointing vector:
  503.       pvec_y = xdot * sine + ydot * cosine;
  504.       // compute dot product, rel. position & pointing vectors:
  505.       dot = pvec_x * dx + pvec_y * dy;
  506.       mag_prod = separation * v;    // p_vec and v vectors are same length
  507.       if(dot > .94 * mag_prod)      // test based on properties of dot product
  508.          dead_ahead = 1;            // there is a car more-or-less dead ahead
  509.       if(separation > CARLEN)    // If separation is greater than length,
  510.            continue;             // cars cannot be bumping.
  511.       // cars might be bumping, test for that.  Simplified test, assumes
  512.       // cars are parallel.  Also, only test for a car in front, as the
  513.       // other car will test also, and find this car in front.
  514.       if(v < .01)              // to prevent division error just below
  515.          continue;
  516.       temp = dot / v;          // dot/v is longitudinal component of separation
  517.       if(temp > CARLEN || dot < 0.0)
  518.          continue;
  519.       if(separation - temp/separation > CARWID)  // test for lateral separation
  520.          continue;
  521.       //The cars are bumping, reduce speed of rear car, and damage both;
  522.       rel_xdot = xdot - pcar[i]->xdot;
  523.       rel_ydot = ydot - pcar[i]->ydot;
  524.       temp = rel_xdot * rel_xdot + rel_ydot * rel_ydot;  //impact energy
  525.       damage += .35 * temp;
  526.       pcar[i]->damage += .2 * temp;   // less damage to car in front
  527.       xdot *= .8;
  528.       ydot *= .8;
  529.    }
  530.  
  531.    int it = 0;
  532.    VC:    // maybe loop to control power (we don't permit P > PM)
  533.  
  534.    Ln = -vc * sine;   Lt = v - vc * cosine; // vector sum to compute slip vector
  535.    l = vec_mag(Lt, Ln);                     // compute slip speed
  536.    F = M * g * friction(l);                 // compute friction force from track
  537.    D = DRAG_CON * v * v;                    // air drag force
  538.    if(offroad)  {                       // if the car is off the track,
  539.       D = (0.6 + .008 * v) * M * g;     // add a lot more resistance
  540.       if(veryoffroad)
  541.          D += 1.7 * M * g;
  542.    }
  543.    if(l < .0001)              // to prevent possible division by zero
  544.       Fn = Ft = 0.0;
  545.    else  {
  546.       Fn = -F * Ln/l;      // compute components of force vector
  547.       Ft = -F * Lt/l;
  548.    }
  549.    // compute power delivered:
  550.    P =  (vc < 0.0 ? -vc : vc) * (Ft * cosine + Fn * sine);
  551.  
  552.    if(!it)  { 
  553.       power_req = P/PM;  // Tell the driver how much power it requested.
  554.       if(P > PM) {       // If the request was too high, reduce it to 100% pwr.
  555.           ++it;
  556.           vc = zbrent(sine, cosine, v, v * cosine, vc, .006);
  557.           goto VC;
  558.       }
  559.    }
  560.  
  561.    // put some randomness in the magnitude of the traction force, F:
  562.    if(Ft != 0.0) // This might be set to 0.0 above, in which case F will be zero
  563.       temp = rfriction(l) * M * g / F;  // ratio of new to original force
  564.    else
  565.       temp = 1.0;
  566.  
  567.    // compute centripetal and tangential acceleration components:
  568.    cen_a = Fn * temp / M;
  569.    tan_a = (Ft * temp - D) / M;
  570.    if(offroad)  {        // this makes it stop when offroad and reversed
  571.       damage += int(tan_a * tan_a + cen_a * cen_a) / 10;
  572.       if(damage > 30000) {
  573.          ++out_count;
  574.          out = 1;
  575.          return;
  576.       }
  577.    }
  578.    if(v < .0001)                         // prevent division by zero
  579.       adot = sine = cosine = 0.0;
  580.    else  {
  581.      adot = cen_a / v;                         // angular velocity
  582.      sine = ydot/v;   cosine = xdot/v;         // direction of motion
  583.    }
  584.    x_a = tan_a * cosine - cen_a * sine;      // x & y components of acceleration
  585.    y_a = cen_a * cosine + tan_a * sine;
  586.    xdot += x_a * delta_time;                 // update the state vector:
  587.    ydot += y_a * delta_time;
  588.    x += (xdot + .5 * x_a * delta_time) * delta_time;
  589.    y += (ydot + .5 * y_a * delta_time) * delta_time;
  590.    if(v >= .0001)
  591.       ang = atan2(ydot,xdot);                   // new orientation angle
  592. }
  593.  
  594. void Car::draw_car(void)  // Calls drawcar() twice, once to erase, once to draw
  595. {
  596.    if(out)       // Don't redraw a car that is out of the race.
  597.       return;
  598.    drawcar(prex, prey, prang, TRACK_COLOR, TRACK_COLOR);     // erase old one
  599.    drawcar(x, y, prang = ang+alpha, nose_color, tail_color); // draw new one
  600.    prex = x;   prey = y;     // save these to erase car next time
  601. }
  602.  
  603. // The Car constructor:
  604. Car::Car(colors use_these, double x_pos, double y_pos, double alf_ang, int i)
  605. {
  606.    which = i;
  607.    starting = 1;
  608.    cntrl = drivers[i];
  609.    out = lap_flag = seg_id = dead_ahead = 0;
  610.    init_flag = damage = 0;
  611.    laps = -1;    // to become 0 crossing the finish line at start of race
  612.    to_end = trackin[0].length;
  613.    alpha = ydot = prang = ang = prev_alpha = 0.0;
  614.    speed_avg = speed_max = 0;
  615.    nose_color = use_these.nose;
  616.    tail_color = use_these.tail;
  617.    prex = x = x_pos;
  618.    prey = y = y_pos;
  619.    xdot = vc = STARTING_SPEED;
  620.    ang = alf_ang;
  621.    power_req = .9;
  622.    data_ptr = (void*)new char[512];
  623.    if(!no_display)
  624.       draw_car();    // draw the new car on the screen
  625. }
  626.  
  627. Car::~Car(void)
  628. {
  629.    if(!no_display)
  630.       drawcar(prex, prey, prang, TRACK_COLOR, TRACK_COLOR);  // erase old car
  631.    delete [] (char *)data_ptr;
  632. }
  633.  
  634. // This function uses the current state of the car, and the current
  635. // track segment data, to compute the car's local situation as seen by
  636. // the driver.  (see struct situation)
  637. situation Car::observe(rel_state* rel_vec_ptr)
  638. {
  639.    double rad;                // current radius
  640.    double dx, dy, xp, yp;
  641.    double sine, cosine, dot;  // dot used for dot product of two vectors
  642.    double temp;
  643.    situation s;
  644.    int nex_seg;         // segment ID of the next segment
  645.    double separation, dxdot, dydot;
  646.    int i, k, m, other_seg;
  647.    // These two arrays describe the three closest cars:
  648.    // element 0 is for the closest, element 1 next, element two after that.
  649.    int closest[] = { 999, 999, 999 };       // their ID's, initially too big
  650.    double how_close[] = { 1e5, 1e5, 1e5 };  // initially very large distances
  651.    int flag = 1;        // controls possible repetition of calculations due to
  652.                         // completion of a lap.
  653.    if(out)            // This gets set if the car is stuck and off the track
  654.       return(s);
  655.  
  656.    s.nearby = rel_vec_ptr;
  657.    s.v       = vec_mag(xdot, ydot);         // the actual speed
  658.    s.dead_ahead = dead_ahead;         // copy the value set by move_car()
  659.    s.power_req = power_req;           // copy the value set by move_car()
  660.    // Computations are base on the right wall of track, which is described
  661.    // by trackout[], except radii are based on the smaller, or inner, curve.
  662.  
  663.    while(flag)  {  // This loop repeats only when a segment boundary is crossed,
  664.                    //   in which case it repeats once:
  665.  
  666.    sine = sin(temp = trackout[seg_id].beg_ang);     // track direction
  667.    cosine = cos(temp);
  668.    if((nex_seg = seg_id + 1) == NSEG)               // which segment is next
  669.       nex_seg = 0;
  670.    s.nex_len = trackout[nex_seg].length;            // length and radius
  671.    s.nex_rad = trackin[nex_seg].radius;             // of next segment
  672.    if(s.nex_rad < 0.0)                              // always use smaller radius
  673.       s.nex_rad = trackout[nex_seg].radius;
  674.    if(++nex_seg == NSEG)
  675.       nex_seg = 0;
  676.    s.after_rad = trackin[nex_seg].radius;         // and the one after that
  677.    if(s.after_rad < 0.0)                          // always use smaller radius
  678.       s.after_rad = trackout[nex_seg].radius;
  679.    s.cur_len = trackout[seg_id].length;           // copy these two fields:
  680.    s.cur_rad = rad = trackin[seg_id].radius;  // rt. turn will use trackout
  681.    if(seg_id != 0)      // This is used to tell when the finish line is 
  682.       lap_flag = 0;     // crossed, thus completing each lap.     
  683.  
  684.    if(rad == 0) {   // if current segment is straight,
  685.       // xp and yp locate the car with respect to the beginning of the right
  686.       // hand wall of the straight segment.  calculate them:
  687.       dx = x - trackout[seg_id].beg_x;
  688.       dy = y - trackout[seg_id].beg_y;
  689.       xp = dx * cosine + dy * sine;
  690.       yp = dy * cosine - dx * sine;
  691.       s.to_rgt = yp;                   // fill in to_rgt and to_end:
  692.       s.to_end = s.cur_len - xp;
  693.  
  694.       if(seg_id == 0)
  695.          if(xp > FINISH * s.cur_len && !lap_flag) {
  696.             lap_flag = 1;        // This means the line crossing was noted.
  697.             if(++laps == lap_count)
  698.                ++done_count;
  699.             if(!no_display)
  700.                lapper(which, laps);  // display the new lap count
  701.             lap[which] = 1;
  702.             if(laps == 0)       // record when the starting line is crossed:
  703.                 start_time = time_count;
  704.             else      // update overall average speed:
  705.                 speed_avg = length * laps / (time_count-start_time);
  706.          }
  707.  
  708.       // here we make sure we are still in the same segment:
  709.       if(s.to_end <= 0.0)  {           // see if a lap has been completed,
  710.          if(++seg_id == NSEG)
  711.             seg_id = 0;
  712.          continue;           // repeat the loop in context of next segment
  713.       }
  714.       s.to_lft = width - yp;           // fill in to_lft & cur_rad:
  715.       s.cur_rad = 0.0;
  716.       s.vn      = ydot * cosine - xdot * sine;  // compute cross-track speed
  717.       s.backward = (xdot * cosine + ydot * sine < 0.0);
  718.    }
  719.    else if(rad > 0.0) {      // when current segment is a left turn:
  720.       dx = x - trackout[seg_id].cen_x; // compute position relative to center
  721.       dy = y - trackout[seg_id].cen_y;
  722.       temp = atan2(dy, dx);           // this is the current angular position
  723.       s.to_end = trackout[seg_id].end_ang - temp - PI/2.0;  // this is an angle
  724.       if(s.to_end > 1.5 * PI)
  725.          s.to_end -= 2.0 * PI;
  726.       else if(s.to_end < -.5 * PI)
  727.          s.to_end += 2.0 * PI;
  728.       if(s.to_end <= 0.0)  {           // Handle segment crossing:
  729.          if(++seg_id == NSEG)
  730.             seg_id = 0;                // going from last segment to 1st
  731.          continue;
  732.       }
  733.       s.to_lft = vec_mag(dx, dy) - rad;
  734.       s.to_rgt = width - s.to_lft;
  735.       s.vn = (-xdot * dx - ydot * dy)/vec_mag(dx, dy);   // a trig thing
  736.       s.backward = (ydot * dx - xdot * dy < 0.0);
  737.    }
  738.    else {
  739.       s.cur_rad = rad = trackout[seg_id].radius;  // rt. turn needs trackout
  740.       dx = x - trackout[seg_id].cen_x; // compute position relative to center
  741.       dy = y - trackout[seg_id].cen_y;
  742.       temp = atan2(dy, dx);           // this is the current angular position
  743.       s.to_end = -trackout[seg_id].end_ang + temp - PI/2.0;  // this is an angle
  744.       if(s.to_end < -.5 * PI)
  745.          s.to_end += 2.0 * PI;
  746.       else if(s.to_end >= 1.5 * PI)
  747.          s.to_end -= 2.0 * PI;
  748.       if(s.to_end <= 0.0)  {           // Handle segment transistion:
  749.          if(++seg_id == NSEG)
  750.             seg_id = 0;
  751.          continue;
  752.       }
  753.       s.to_rgt = vec_mag(dx, dy) + rad;
  754.       s.to_lft = width - s.to_rgt;
  755.       s.vn = (xdot * dx + ydot * dy)/vec_mag(dx, dy);   // a trig thing
  756.       s.backward = (xdot * dy - ydot * dx < 0.0);
  757.    }
  758.  
  759.    // If we get this far, we do not repeat the big loop.
  760.    flag = 0;
  761.    }  // end while(flag) loop
  762.    if(s.backward)  {
  763.       to_end = s.to_end;
  764.       offroad = (s.to_lft < 0.0 || s.to_rgt < 0.0);   // maybe set offroad flag
  765.       veryoffroad = (s.to_lft < -width || s.to_rgt < -width); // maybe veryoffroad
  766.       return s;
  767.    }
  768.  
  769.    offroad = (s.to_lft < 0.0 || s.to_rgt < 0.0);   // maybe set offroad flag
  770.    veryoffroad = (s.to_lft < -width || s.to_rgt < -width); // maybe veryoffroad
  771.    to_end = s.to_end;                       // fill in this field in Car object
  772.  
  773.    // find three closest cars in front:
  774.    // "front", here, is direction of velocity vector, not pointing vector.
  775.    for(i=0; i<car_count; i++) {     // check for cars nearby
  776.       if(i == which) continue;         // ignore oneself
  777.       other_seg = pcar[i]->seg_id;     // only consider present and next segment
  778.       if(!(seg_id == other_seg || incseg(seg_id) == other_seg))
  779.           continue;
  780.       dx = pcar[i]->x - x;      // components of vector to other car
  781.       dy = pcar[i]->y - y;
  782.       separation = vec_mag(dx, dy);      // distance to the other car
  783.       // compute dot product, rel. position & velocity vectors:
  784.       dot = xdot * dx + ydot * dy;
  785.       if(dot < 0.0)
  786.          continue;              // Ignore cars behind you.
  787.       for(k=0; k<3; k++)  {
  788.          if(separation < how_close[k])  {
  789.             for(m = 2; m > k; m--) {
  790.                closest[m] = closest[m-1];
  791.                how_close[m] = how_close[m-1];
  792.             }
  793.             closest[k] = i;
  794.             how_close[k] = separation;
  795.             break;
  796.          }
  797.       }
  798.    }
  799.      // now compute local relative state vector for those three cars:
  800.    for(k=0; k<3; k++)  {
  801.       if(closest[k] > car_count) {  // This is when there are less than 3
  802.          for(; k<3; k++)
  803.             s.nearby[k].who = 999;
  804.          break;
  805.       }
  806.       i = closest[k];
  807.       dx = pcar[i]->x - x;      // components of relative position vector
  808.       dy = pcar[i]->y - y;
  809.       dxdot = pcar[i]->xdot - xdot; // components of relative velocity
  810.       dydot = pcar[i]->ydot - ydot;
  811.       // the relative vectors must be found wrt to velocity vector frame
  812.       // we need sine and cosine of rotation of axes:
  813.       if(s.v > 1e-7)  {        // prevent division by 0
  814.          sine = - xdot / s.v;                 // direction of velocity
  815.          cosine = ydot / s.v;                  // wrt the x axis.
  816.       }
  817.       else  {
  818.          sine = -1.0;
  819.          cosine = 0.0;
  820.       }
  821.       s.nearby[k].who = i;
  822.       s.nearby[k].rel_x = cosine * dx + sine * dy;
  823.       s.nearby[k].rel_y = cosine * dy - sine * dx;
  824.       s.nearby[k].rel_xdot = cosine * dxdot + sine * dydot;
  825.       s.nearby[k].rel_ydot = cosine * dydot - sine * dxdot;
  826.    }
  827.  
  828.    return s;
  829. }
  830.  
  831. // create a default situation vector to pass to control() during initializaton
  832. situation fill_situation(rel_state* rel_state_vec_ptr)
  833. {
  834.    situation result;
  835.  
  836.    result.cur_rad = 0.0;
  837.    result.cur_len = 1.0;
  838.    result.to_lft =  1.0;    
  839.    result.to_rgt =  1.0;  
  840.    result.to_end =  1.0;    
  841.    result.v = STARTING_SPEED;         
  842.    result.vn = 0.0;        
  843.    result.nex_len = 1.0;   
  844.    result.nex_rad = 1.0;   
  845.    result.after_rad = 0.0; 
  846.    result.power_req = 1.0; 
  847.    result.dead_ahead = 0;   
  848.    result.backward = 0;     
  849.    result.nearby = rel_state_vec_ptr;
  850.    for(int k=0; k<3; k++)
  851.       result.nearby[k].who = 999;
  852.  
  853.    return result;
  854. }
  855.  
  856. // gets the names from the robot drivers and stores them in RAM.
  857. // nam_ptr[] is the array of pointers to the names.
  858. // some elements of nam_ptr[] may point to the same place; others may be 0;
  859. void get_names(void)
  860. {
  861.    int i, j;
  862.    situation s;                    // to pass data from observe() to control()
  863.    rel_state rel_state_vec[3];     // needed by fill_situation() function
  864.    con_vec (*func_ptr)(situation);
  865.  
  866.    for(i=0; i<MAXCARS; i++) {
  867.       glob_name[0] = 0;        // to check later to see if driver filled it
  868.       // fill in s to avoid div. by zero in driver.
  869.       s = fill_situation(rel_state_vec);
  870.       // The first call to a robot driver may put its name in glob_name.
  871.       func_ptr = drovers[i];
  872.       (*func_ptr)(s);
  873.       if(glob_name[0] != 0)  {   // add new name to nam_ptr[] array:
  874.          nam_ptr[i] = new char[strlen(glob_name) + 1];
  875.          strcpy(nam_ptr[i], glob_name);
  876.          *(nam_ptr[i] + 8) = '\0';   // chop any names greater than 8 chars.
  877.       }
  878.       else                       // if no name was given, see if there is
  879.          for(j=i-1; j>=0; j--)   // already a name for that driver:
  880.             if(drovers[i] == drovers[j]) {
  881.                nam_ptr[i] = nam_ptr[j];
  882.                break;
  883.             }
  884.  
  885.    }
  886. }
  887.  
  888. // Will put a random permutation of the robot drivers in "drovers[]" 
  889. // into the "drivers[]" array.
  890. void pick_random_order(void)
  891. {
  892.      int selected[MAXCARS];  // marks the points as they are picked
  893.      int racers[MAXCARS];    // the random permutation goes here
  894.      int i, j, k, count;
  895.  
  896.      for(i=0; i<car_count; i++) {     // initialize selected[] to all zeroes:
  897.          selected[i] = 0;    // (meaning no points are picked)
  898.      }
  899.  
  900.      // for each element of rptr, j is no. of alternates
  901.      for(j=car_count; j>0; j--) {  // remaining.  here we pick one of them:
  902.         k = (j > 1) ? ((int)random(j)) : (0);       // k chooses among the remaining pts.
  903.          for(i=0, count=0; i<car_count; i++)
  904.             if(!selected[i])            // if not already picked,
  905.                if(count++ == k)  {
  906.                    selected[i] = 1;     // marks this point as taken
  907.                    racers[j-1] = i;       // puts it into the genome
  908.                }
  909.      }
  910.      // now copy the selected members of drovers[] into drivers[],
  911.      // and the same for their colors & names:
  912.      for(i=0; i<car_count; i++) {
  913.         drivers[i] = drovers[racers[i]];
  914.         car_colors[i] = car_clrs[racers[i]];
  915.         namptr[i] = nam_ptr[racers[i]];
  916.      }
  917.  
  918. }
  919.  
  920. // copy drovers[] into drivers[], and the same for their colors & names:
  921. void copy_drivers(void)
  922. {
  923.    int i;
  924.    for(i=0; i<car_count; i++) {
  925.       drivers[i] = drovers[i];
  926.       car_colors[i] = car_clrs[i];
  927.       namptr[i] = nam_ptr[i];
  928.    }
  929.  
  930. }
  931.  
  932. void reverse_order(void)
  933. {
  934.    colors temp_color;
  935.    con_vec(*temp_driver)(situation);
  936.    char *temp_ptr;
  937.    for(int i=0; i<car_count/2; i++) {
  938.         temp_driver = drivers[i];
  939.         drivers[i] = drivers[car_count - 1 - i];
  940.         drivers[car_count - 1 - i] = temp_driver;
  941.         temp_ptr = namptr[i];
  942.         namptr[i] = namptr[car_count - 1 - i];
  943.         namptr[car_count - 1 - i] = temp_ptr;
  944.         temp_color = car_colors[i];
  945.         car_colors[i] = car_colors[car_count - 1 - i];
  946.         car_colors[car_count - 1 - i] = temp_color;
  947.    }
  948. }
  949.  
  950. main(int argc, char* argv[])       // 1st arg is no. of cars in race
  951. {                                  // 2nd arg is no. of laps to race
  952.    int i, j, ml;
  953.    double x, y, dx, dy;            // used only for initial positions of cars
  954.    situation s;                    // to pass data from observe() to control()
  955.    int num_disp;   // how many cars to display in leaders area of scoreboard
  956.    int starting;        // indicates the race is just starting
  957.    rel_state rel_state_vec[3];    // contains up to 3 relative state vectors
  958.  
  959.    glob_name = new char[33];
  960.    strcpy(glob_name, "Not yet filled in, 32 characters");
  961.  
  962.    get_names();  // call all the robot drivers to fill in the nam_ptr[] array.
  963.  
  964.    // get argument values into car_count and lap_count global variables
  965.    get_args(argc, argv);       // sets car_count and lap_count
  966.    num_disp = (car_count < 5) ? car_count : 5;  // show up to 5
  967.    if(no_display)
  968.       real_speed = 0;
  969.  
  970.    // setup the graphics display, also compute track data:
  971.    graph_setup();
  972.  
  973.    randomizer();
  974.    report_overall(car_count, lap_count, nam_ptr);
  975.    // Main loop - Once through here for every complete race
  976.  for(ml=0; ml<race_count; ++ml)  {
  977.    done_count = 0;  // incremented by each car that finishes the race
  978.    out_count = 0;   // incremented by each car that crashes
  979.    time_count = 0.0;  // elapsed time, seconds
  980.    starting = 1;
  981.  
  982.    if(keep_order)
  983.       copy_drivers();
  984.    else
  985.       if(!(ml%2))
  986.          pick_random_order();  // arrange the starting positions
  987.       else
  988.          reverse_order();      // reverse positions for 2nd race
  989.  
  990.    // create cars (instances of car class)
  991.     dy = width/4;          // the dy and dx stuff is for arranging
  992.    dx = 2.5 * CARLEN;     // the cars in their starting positions
  993.    x = trackout[0].beg_x - dx;                // We assume that segment
  994.    for(i=0; i<car_count; i++) {                    // 0 is straight, and
  995.       if(!(i%4)) {                                 // parallel to the x axis.
  996.          y = trackout[0].beg_y + dy/2;             // Cars start on seg. 0.
  997.          x += dx;
  998.       }
  999.       else
  1000.          y += dy;
  1001.       pcar[i] = new Car(car_colors[i], x, y, 0, i);   // This is the key part!
  1002.    }
  1003.    if(!car_count) {  //When zero cars are requested, we only show the track.
  1004.       if(no_display)  {
  1005.          cout << "Zero cars were requested." << endl;
  1006.          exit(0);
  1007.       }
  1008.       get_ch();         // this executes only when 0 cars are requested
  1009.       resume_normal_display();
  1010.       exit(0);
  1011.    }
  1012.  
  1013.    // Put up the scoreboard and leader board:
  1014.    if(!no_display)
  1015.       scoreboard();
  1016.  
  1017.    done_count = 0;
  1018.    one_tick(1);
  1019.    order = new int[car_count];    //setup the order[] array:
  1020.    new_data = new int[num_disp];  //setup the new_data[] array:
  1021.    for(i=0, j=car_count-1; i<car_count; i++, j--) { // initialize order[] array
  1022.       order[i] = j;            // must correspond with initial track positions
  1023.       lap[i] = 1;
  1024.    }
  1025.    if(!no_display)
  1026.       if(get_ch() == ESC)  // Don't actually start the race until someone hits a key.
  1027.          break;               // Any key except ESC begins the race.
  1028.    // BEGIN THE RACE!
  1029.    // Note that the lap count on the scoreboard is updated by the
  1030.    // observe() function, which call lapper() to do that.
  1031.    if(no_display)
  1032.       cout << "Beginning race " << ml+1 << endl;
  1033.    while(1) {                                // main loop of the race:
  1034.       for(i=0; i<car_count; i++) {           // for each car:
  1035.          s = pcar[i]->observe(rel_state_vec); // compute its local situation
  1036.          pcar[i]->control(s);                // compute a control vector
  1037.       }
  1038.       for(i=0; i<car_count; i++) {     // for each car:
  1039.          pcar[i]->move_car();                // update state of car
  1040.       }
  1041.       if(!no_display)
  1042.          for(i=0; i<car_count; i++)       // for each car:
  1043.            pcar[i]->draw_car();             // update screen image of car
  1044.       time_count += delta_time;        // Advance the simulated time.
  1045.       sortem(num_disp);      // maintains the order[] and new_data[] arrays
  1046.       // For any line flagged by new_data[], update that line in "LEADERS" area:
  1047.       for(i=0; i<num_disp; i++)
  1048.          if(starting || new_data[i] || lap[order[i]]) {
  1049.             lap[order[i]] = 0;
  1050.             if(!no_display)
  1051.                leaders(i);
  1052.          }
  1053.       if(!no_display)
  1054.          refresh_finish_line();
  1055.       if(kb_hit())    // respond to the keyboard if its hit
  1056.          if(get_ch() == ESC)     // ESC key will end the race.
  1057.             break;
  1058.       // check for race over:
  1059.       if(done_count >= 2 || done_count >= car_count - out_count)
  1060.          break;
  1061.       if(real_speed)
  1062.          one_tick(0);      // wait here till the next clock tick
  1063.       starting = 0;       // the race is on!
  1064.    }
  1065.    if(no_display)
  1066.       cout << "End of race " << ml+1 << endl;
  1067.  
  1068.    report_results(ml+1, order, namptr, pcar);
  1069.  
  1070.    if(!no_display)
  1071.       if(get_ch() == ESC)  // Continue showing end of race until someone hits a key.
  1072.           break;
  1073.  
  1074.    for(i=0; i<car_count; i++) { // delete all the car objects
  1075.       delete pcar[i];
  1076.    }
  1077.  } // End of Main Loop.
  1078.  
  1079.    /* clean up, end graphics, back to normal */
  1080.    resume_normal_display();
  1081.    RAM_report();
  1082.    return 0;  // all done with execution, return to DOS.
  1083. }
  1084.  
  1085.